package com.diaohw.module.file.service.impl;

import com.diaohw.module.file.mapper.AccountFileMapper;
import com.diaohw.module.file.mapper.CatalogMapper;
import com.diaohw.module.file.mapper.FileTagMapper;
import com.diaohw.module.file.mapper.TagMapper;
import com.diaohw.module.file.obj.convert.TagConvert;
import com.diaohw.module.file.obj.dto.AccountFileDTO;
import com.diaohw.module.file.obj.dto.FileTagDTO;
import com.diaohw.module.file.obj.entity.AccountFileDO;
import com.diaohw.module.file.obj.entity.CatalogDO;
import com.diaohw.module.file.obj.entity.FileTagDO;
import com.diaohw.module.file.obj.query.AccountFileQuery;
import com.diaohw.module.file.obj.tables.FileTagDef;
import com.diaohw.module.file.obj.vo.AccountFileVO;
import com.diaohw.module.file.obj.vo.FileVO;
import com.diaohw.module.file.obj.vo.TagVO;
import com.diaohw.module.file.service.AccountFileService;
import com.diaohw.module.file.service.FileService;
import com.diaohw.module.file.service.FileTagService;
import com.diaohw.platform.common.obj.page.PageResultVo;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户上传表 服务实现类
 * </p>
 *
 * @author D-Hw
 * @since 2024-11-05
 */
@Service
public class AccountFileImpl implements AccountFileService {

    @Resource
    private FileService fileService;
    @Resource
    private CatalogMapper catalogMapper;
    @Resource
    private AccountFileMapper accountFileMapper;
    @Resource
    private FileTagService fileTagService;
    @Resource
    private FileTagMapper fileTagMapper;
    @Resource
    private TagMapper tagMapper;

    @Override
    public AccountFileVO create(AccountFileDTO data, MultipartFile file) {
        CatalogDO catalog = catalogMapper.selectOneById(data.getCatalogId());
        FileVO upload = fileService.upload(data.getStorage(), file);
        AccountFileDO model = new AccountFileDO();
        model.setCatalogId(catalog.getId());
        model.setRemark(data.getRemark());
        model.setFilename(upload.getFilename());
        model.setFileId(upload.getId());
        model.setLength(upload.getLength());
        model.setExt(upload.getExt());
        accountFileMapper.insert(model);
        fileTag(data.getTagIds(), model.getId());
        return INSTANCE.entityToVo(model);
    }

    @Override
    public AccountFileVO update(String id, AccountFileDTO data) {
        AccountFileDO model = INSTANCE.dtoToEntity(data);
        model.setId(id);
        accountFileMapper.update(model);
        fileTagService.delete(FileTagDef.FILE_TAGS.FILE_ID, List.of(id));
        fileTag(data.getTagIds(), id);
        return INSTANCE.entityToVo(model);
    }

    @Override
    public Boolean delete(String id) {
        fileTagService.delete(FileTagDef.FILE_TAGS.FILE_ID, List.of(id));
        return accountFileMapper.deleteById(id) > 0;
    }

    @Override
    public Boolean delete(List<String> ids) {
        fileTagService.delete(FileTagDef.FILE_TAGS.FILE_ID, ids);
        return accountFileMapper.deleteBatchByIds(ids) > 0;
    }

    @Override
    public AccountFileVO detail(String id) {
        AccountFileDO model = accountFileMapper.selectOneById(id);
        return selectTags(List.of(model)).get(0);
    }

    @Override
    public List<AccountFileVO> findByList(AccountFileQuery query) {
        QueryWrapper wrapper = AccountFileMapper.createWrapper(query);
        List<AccountFileDO> list = accountFileMapper.selectListByQuery(wrapper);
        return selectTags(list);
    }

    @Override
    public PageResultVo<AccountFileVO> findByPage(AccountFileQuery query) {
        QueryWrapper wrapper = AccountFileMapper.createWrapper(query);
        Page<AccountFileDO> page = this.accountFileMapper.paginate(query.getPageIndex(), query.getPageSize(), query.getTotalRow(), wrapper);
        return PageResultVo.create(page.getTotalRow(), selectTags(page.getRecords()));
    }


    @Override
    public Boolean tag(FileTagDTO data) {
        List<FileTagDO> list = data.getFileIds().parallelStream().map(fileId -> {
            FileTagDO tag = new FileTagDO();
            tag.setFileId(fileId);
            tag.setTagId(data.getTagId());
            return tag;
        }).toList();
        fileTagService.delete(FileTagDef.FILE_TAGS.FILE_ID, data.getFileIds());
        fileTagMapper.insertBatch(list);
        return Boolean.TRUE;
    }

    private void fileTag(List<String> tagIds, String fileId) {
        if (tagIds == null || tagIds.isEmpty()) {
            return;
        }
        List<FileTagDO> list = tagIds.stream().map(tagId -> {
            FileTagDO fileTag = new FileTagDO();
            fileTag.setTagId(tagId);
            fileTag.setFileId(fileId);
            return fileTag;
        }).toList();
        fileTagMapper.insertBatch(list);
    }


    private List<AccountFileVO> selectTags(List<AccountFileDO> rows) {
        if (rows.isEmpty()) {
            return List.of();
        }
        List<AccountFileVO> list = INSTANCE.entityToVo(rows);

        List<String> fileIds = rows.parallelStream().map(AccountFileDO::getId).toList();

        List<FileTagDO> fileTags = fileTagMapper.selectListByQuery(QueryWrapper.create().in(FileTagDO::getFileId, fileIds));
        if (fileTags.isEmpty()) {
            return list;
        }
        Set<String> tagIds = fileTags.stream().map(FileTagDO::getTagId).collect(Collectors.toSet());
        Map<String, TagVO> map = TagConvert.INSTANCE.entityToVo(tagMapper.selectListByIds(tagIds)).stream().collect(Collectors.toMap(TagVO::getId, e -> e, (k1, k2) -> k1));
        Map<String, Set<String>> collect = fileTags.stream().collect(Collectors.groupingBy(FileTagDO::getFileId, Collectors.mapping(FileTagDO::getTagId, Collectors.toSet())));
        for (AccountFileVO vo : list) {

            if (!collect.containsKey(vo.getId())) {
                continue;
            }

            List<TagVO> tags = collect.get(vo.getId()).stream().filter(map::containsKey).map(map::get).toList();
            vo.setTags(tags);
        }
        return list;

    }


}
